####

# @project: UbunTube
# @version = 0.1a
# @site UbunTube http://www.ubuntube.tk
# @copyright (c) 2009 Mohamed Nashmi
# @license http://opensource.org/licenses/gpl-license.php GNU Public License

####

import threading
import subprocess
import gtk
import gobject
import time

import Debug




class Mplayer(gobject.GObject, threading.Thread):
    """Mplayer is the Class to be used when playing a video. It contains a subprocess
    and a gtk.Drawing area to draw the Mplayer movie.  It also inherits Thread to 
    prevent freezing issues"""
    def __init__(self, video):
        """Creates the Mplayer object
        
        video = The parent Video Object
        """
        threading.Thread.__init__(self)
        self.__gobject_init__()
        #:The gtk.DrawingArea which will hold the Mplayer process
        self.canvas = gtk.DrawingArea()
        #:The page number of the Video in the Notebook
        self.page = -1
        #:The parent Video Object
        self.video = video
        self.canvas.connect('expose-event', self.created)
        #: Boolean specifying if the video is playing or not
        self.playing = False
        self.started = False
        #: gtk.VBox which holds the canvas and the control bar
        self.UI = gtk.VBox()
        #: The Mplayer subprocess
        self.process = None
        #: The Mplayer.subprocess.stdout to read the position of the video
        self.scrollerReader = None
        #:The Video Controlling bar which is an instance of ControlBar
        self.videoController = ControlBar(self)
        self.UI.pack_start(self.canvas)
        self.UI.pack_start(self.videoController, False, False)
        
        
    def created(self, widget, event):
        """Callback function for the expose-event of the gtk.DrawingArea. When the
        drawingarea is first created, we start the thread to play the video. 
        
        It checks if the video is already playing, because the callback is called 
        for each mplayer frame.
        """ 
        if not self.playing and self.video:
            self.canvas.xid = widget.window.xid
            self.playing = True
            if not self.started:
                self.start()
            else:
                print "thrad started, just play.."
                self.loadVideo()
        else:
            Debug.debug("Calling")
            
                
    def run(self):
        """Overrides the threading.Thread function. This calls the mplayer
        process and specifies the gtk.Drawing area for the mplayer to play in.
        """
        self.started = True
        self.loadVideo()
        
                  
    def loadVideo(self):
        command = "mplayer -vo xv -wid %i -slave -idle -zoom" % (self.canvas.xid)
        try:
            Debug.debug("PROCESS")
            self.process = subprocess.Popen([command],
                                    shell=True, 
                                    stdin = subprocess.PIPE,
                                    stdout = subprocess.PIPE,
                                    bufsize=1
                                    )
            self.videoController.playVideo(self.process)
        except Exception, e:
            Debug.debug("Error", e)
        

class ControlBar(gtk.HBox):
    """Inherits a gtk.HBox to implement a control bar for the Video. This holds a 
    rewind, play/pause, forward buttons and a gtk.HScale.
    """
    def __init__(self, Mplayer):
        """Creates the ControlBar object.
        
        Mplayer = the parent Mplayer object
        """
        
        gtk.HBox.__init__(self)
        #: Reference to the parent Mplayer object
        self.mplayer = Mplayer
        #:Provides a short link to the Mplayer.process variable
        self.process = None
        #:Boolean specifiying if the video started playing.
        self.playing = False
        #:Boolean specifiying if the video is paused or playing. Prevents the HSalce from forcing the video to play
        self.paused = False
        rewindImg = gtk.image_new_from_stock(gtk.STOCK_MEDIA_REWIND, gtk.ICON_SIZE_MENU)
        rewind = gtk.Button()
        rewind.add(rewindImg)
        rewind.connect('clicked', self.rewindVideo)
         
        previousImg = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PREVIOUS, gtk.ICON_SIZE_MENU)
        previous = gtk.Button()
        previous.add(previousImg)
        previous.connect('clicked', self.previousVideo)
        
        
        pauseImg = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_MENU)
        pause = gtk.ToggleButton()
        pause.connect('toggled', self.pauseVideo)
        pause.add(pauseImg)
        
        nextImg = gtk.image_new_from_stock(gtk.STOCK_MEDIA_NEXT, gtk.ICON_SIZE_MENU)
        next = gtk.Button()
        next.add(nextImg)
        next.connect('clicked', self.nextVideo)
        
        #Scroller
        duration = float(self.mplayer.video.duration)
        #:gtk.Adjustment which holds the Duration of the Video as its Maximum (upper) limit
        self.adj = gtk.Adjustment(0.0, 0.0, duration,  1, 5.0, 1.0)
        #:Hscale which acts as the seekbar for the video
        self.hscale = gtk.HScale(self.adj)
        self.hscale.set_size_request(200, 30)
        self.hscale.set_draw_value(False)
        self.hscale.connect("change-value", self.seekTo)
        #hscale.connect('format-value', self.scrollerText)
        self.pack_start(rewind, False, False)
        self.pack_start(previous, False, False)
        self.pack_start(pause, False, False)
        self.pack_start(next, False, False)
        self.pack_start(self.hscale)
        self.show_all()
        
    
    
    def seekTo(self,range, scale, seekTo):
        """Handles if the user drags the slider to navigation in the movie"""
        self.seekVideo(seekTo)
        
    def playVideo(self, process):
        """Plays the specified video by issuing the appropriate command to 
        the parent Mplayer process
        
        It creates a timeout_add to update the seekbar's time
        process = the parent Mplayer.process
        """
        try:
            self.process = process
            self.process.stdin.write("loadfile '%s'\n" % self.mplayer.video.retriever.file)
            self.playing = True
            gobject.timeout_add(30, self.updateScroller)
        except Exception, e:
            Debug.debug("DIDNT write", e)
    
    def updateScroller(self):
        """Updates the Hscale to the appropriate position of the movie
        by setting the value of the current position
        
        It also handles if the video has reached its end
        """
        if not self.paused:
            self.process.stdin.write("get_time_pos\n")
            line = self.process.stdout.readline()
            if line and line.startswith("ANS_TIME_POSITION"):
                pos = float(line.split("ANS_TIME_POSITION=")[1])
                self.hscale.set_value(pos)
                if int(pos) == int(self.adj.upper):
                    Debug.debug("DONE")
                    self.mplayer.page
                    return False
        return True
        
    def seekVideo(self, position):
        """Seeks to a specific position in seconds in the video being played"""
        self.process.stdin.write('seek %s 2\n' %position)
        
    def pauseVideo(self, button):
        """Pauses/Plays the video depending on the state of the toggle button"""
        if not button.get_active():
            img = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_MENU)
            self.paused = False
        else:
            self.process.stdin.write('pause\n')
            img = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_MENU)
            self.paused = True
        button.set_image(img)
           
    def rewindVideo(self, button):
        """Rewinds the video to the start"""
        self.process.stdin.write('seek 0 2\n')
        
    
    def previousVideo(self, button):
        """Steps back by 5 seconds in the video"""
        self.process.stdin.write('seek -5 0\n')
        
    def nextVideo(self, button):
        """Steps forward by 5 seconds in the video"""
        self.process.stdin.write('seek +5 0\n')

    def closeVideo(self):
        """Closes a video by quiting the mplayer process, and nullifying
        the parent mplayer process.
        """
        try:
            self.process.stdin.write('quit\n')
            self.playing = False
            self.mplayer.playing = False
            #return True
        except Exception, e:
            Debug.debug("Didnt write QUIT", e)
            
